home *** CD-ROM | disk | FTP | other *** search
/ Ian & Stuart's Australian Mac 1993 September / September 93.iso / Archives / Sound / MIDI / MIDI Utilities / CMU Midi Toolkit / Source / adagio.c next >
C/C++ Source or Header  |  1988-01-29  |  16KB  |  590 lines

  1. /* adagio.c -- user interface to adagio play and record */
  2.  
  3. /*****************************************************************************
  4. *        Change Log
  5. *  Date        | Change
  6. *-----------+-----------------------------------------------------------------
  7. * 12-Mar-86 | Created
  8. * 28-May-86 | Added calls to cmdline.c for command line parsing
  9. *  2-Oct-86 | JMaloney: Converted to Macintosh
  10. *            | Removed -xt, -at, and -metronome switches
  11. *  7-Jan-87 | JMaloney: Converted to Lightspeed C
  12. *            | Moved command loop here from phase2
  13. * 29-Jan-87 | JMaloney: Enabled reading of new score files
  14. * 12-Jan-88 | Dannenberg: new modal command loops, same funtionality
  15. * 29-Jan-88 | Dannenberg: made some user commands case insensitive
  16. *****************************************************************************/
  17.  
  18. #include "switches.h"
  19.  
  20. #ifdef LIGHTSPEED
  21. #include <Proto.h>
  22. #include <StdIO.h>
  23. #include <MemoryMgr.h>
  24. #endif
  25.  
  26. #ifdef MPW
  27. #include <StdIO.h>
  28. #include <Types.h>
  29. #include <Memory.h>
  30. #endif
  31.  
  32. #include "CType.h"
  33. #include "cext.h"
  34. #include "adagio.h"
  35. #include "cmdline.h"
  36. #include "stream.h"
  37. #include "filestream.h"
  38. #include "mpu.h"
  39. #include "noteoff.h"
  40. #include "phase1.h"
  41. #include "phase2.h"
  42. #include "record.h"
  43. #include "userio.h"
  44.  
  45. /****************************************************************************
  46. *
  47. *    command line switches and options
  48. *
  49. ****************************************************************************/
  50.  
  51. #define nswitches 12
  52. private char *switches[nswitches] =
  53.     {"-debug", "-d", "-help", "-h", "-miditrace", "-m", "-trace", "-t",
  54.      "-simulated", "-s", "-print", "-block"};
  55.  
  56. #define noptions 3
  57. private char *options[noptions] = {"-c", "-control", "-tune"};
  58.  
  59. /****************************************************************************
  60. *
  61. *    variables shared with other modules
  62. *
  63. ****************************************************************************/
  64.  
  65. extern boolean musictrace;        /* read by report_modes */
  66. extern boolean miditrace;        /* read by report_modes */
  67.  
  68. /****************************************************************************
  69. *
  70. *    macros and constants
  71. *
  72. ****************************************************************************/
  73.  
  74. #define LINE_SIZE 100
  75.  
  76. /****************************************************************************
  77. *
  78. *    routines private to this module
  79. *
  80. ****************************************************************************/
  81.  
  82. void        cmdline_help(void);
  83. void        cmd_help(void);
  84. void        cmd_loop(event_type);
  85. void        default_response(char);
  86. event_type    new_score(char *);
  87. void        play_mode();
  88. void        record_mode();
  89. void        transcribe_mode();
  90. void        play_pause_loop(event_type, ulong, StreamPtr);
  91. void        report_activity(event_type, ulong);
  92. void        report_modes(void);
  93. void        report_stop(event_type, ulong, int);
  94. void        _main(int argc, char *argv[]);
  95.  
  96.  
  97. static ulong startTime = 0;
  98.  
  99. /****************************************************************************
  100. *                cmdline_help
  101. * Effect:
  102. *    list command line switches and options
  103. ****************************************************************************/
  104.  
  105. private void cmdline_help()
  106. {
  107.     gprintf(TRANS, "adagio [options]\n");
  108.     gprintf(TRANS, "  options are:\n");
  109. /*    gprintf(TRANS, "    -block                  disable MIDI thru\n");*/
  110.     gprintf(TRANS, "    -control (-c) [on|off]  en/disable continuous controller recording\n");
  111.     gprintf(TRANS, "    -debug (-d)             enable verbose debug mode\n");
  112.     gprintf(TRANS, "    -help (-h)              print this message\n");
  113.     gprintf(TRANS, "    -miditrace (-m)         turn on MIDI command trace\n");
  114.     gprintf(TRANS, "    -print                  print note data\n");
  115.     gprintf(TRANS, "    -tune file              use tuning from file\n");
  116.     gprintf(TRANS, "    -simulated (-s)         run with simulated midi device\n");
  117.     gprintf(TRANS, "    -trace (-t)             turn on music operation trace\n\n");
  118.     gprintf(TRANS, "\n");
  119. }
  120.  
  121. /****************************************************************************
  122. *                cmd_help
  123. * Effect:
  124. *    help for command interpreter
  125. ****************************************************************************/
  126.  
  127. private void cmd_help()
  128. {
  129.     gprintf(TRANS, "\n");
  130.     gprintf(TRANS, "  <Return>    play\n");
  131.     gprintf(TRANS, "   q          quit this mode\n");
  132.     gprintf(TRANS, "   b          begin at a particular time\n");
  133.     gprintf(TRANS, "   m          toggle MIDI byte trace mode\n");
  134.     gprintf(TRANS, "   t          toggle music operation trace mode\n");
  135.     gprintf(TRANS, "   s          report operation mode\n");
  136.     gprintf(TRANS, "   u          read a tuning file\n");
  137.     gprintf(TRANS, "   ?,<other>  display this message\n");
  138.     gprintf(TRANS, "\n");
  139. }
  140.  
  141. /****************************************************************************
  142. *                cmd_loop
  143. * Inputs:
  144. *    event_type score: score to play
  145. * Effect:
  146. *    a simple command interpreter for single key commands
  147. ****************************************************************************/
  148.  
  149. private void cmd_loop(score)
  150.     event_type score;
  151. {
  152.     char userinput[100];
  153.     char response;
  154.     boolean done = false;
  155.  
  156.     while (!done) {
  157.         *userinput = 0;
  158.         while (*userinput == 0 || !isalpha(*userinput)) {
  159.             gprintf(TRANS, "Type a for Adagio, t for transcribe, r for record: ");
  160.             gets(userinput);
  161.         }
  162.         response = tolower(*userinput);
  163.         switch (response) {
  164.         case 'a':
  165.             play_mode();
  166.             break;
  167.         case 'r':
  168.             record_mode();
  169.             break;
  170.         case 't':
  171.             transcribe_mode();
  172.             break;
  173.         }
  174.     }
  175. }
  176.  
  177. /* default_response -- handle things common to play, transcribe, record */
  178. /**/
  179. void default_response(response)
  180.   char response;
  181. {
  182.     response = tolower(response);
  183.     switch (response) {
  184.       case 'b':
  185.         {
  186.             char userInput[LINE_SIZE];
  187.  
  188.             gprintf(TRANS, "Type starting time (in hundredths of seconds)\n");
  189.             gets(userInput);
  190.             if (sscanf((byte *) userInput, "%ld", &startTime) != 1) {
  191.                 gprintf(TRANS, "Bad start time!\n");
  192.                 startTime = 0;
  193.             }
  194.         }
  195.         break;
  196.       case 'm':
  197.         tracemidi(!miditrace);
  198.         report_modes();
  199.         break;
  200.       case 's':
  201.         report_modes();
  202.         break;
  203.       case 't':
  204.         trace(!musictrace);
  205.         report_modes();
  206.         break;
  207.       case 'u':
  208.         read_tuning("");
  209.         break;
  210.       case '?':
  211.       default:
  212.         cmd_help();
  213.         break;
  214.     }
  215. }
  216.  
  217.  
  218.  
  219. /****************************************************************************
  220. *                    new_score
  221. * Inputs:
  222. *    char * defaultFile: the file name from command line or NULL
  223. * Returns:
  224. *    event_type: the new score or NULL
  225. * Effect:
  226. *    if defaultFile is not NULL, it is read
  227. *    otherwise new_score prompts for file name and attempts to read that file
  228. *    if the user doesn't supply a file name when prompted or if the
  229. *    open operation fails, the NULL score is returned
  230. * Implementation:
  231. *    any existing score is freed before reading a new score
  232. ****************************************************************************/
  233.  
  234. event_type new_score(defaultFile)
  235.     char *defaultFile;
  236. {
  237.     char inFileName[LINE_SIZE];
  238.     FILE *inFile = NULL;
  239.     StreamPtr inStream;
  240.     event_type newScore;
  241.     ulong notes, controls;
  242.     ulong junk;
  243.  
  244. #ifndef LIGHTSPEED
  245.     if ((defaultFile != NULL) && (strlen(defaultFile) > 0)) {
  246.         strcpy(inFileName, defaultFile);
  247.     } else {
  248.         gprintf(TRANS, "Score file?\n");
  249.         gets(inFileName);
  250.         if (strlen(inFileName) == 0) return NULL;    /* never mind! */
  251.     }
  252. #else
  253.     *inFileName = 0;
  254. #endif
  255.     inFile = fileopen(inFileName, "gio", "r", "Adagio score file");
  256.     inStream = filestream_OpenRead(inFile);
  257.     if (inStream == NULL) {
  258.         /* should never happen */
  259.         gprintf(FATAL,
  260.                 "Implementation: (adagio.c) Could not open input filestream.");
  261.         return NULL;
  262.     }
  263.  
  264.     phase1_FreeMem();    /* free old score, if any */
  265.     newScore = phase1_Parse(inStream, ¬es, &controls);    /* parse input */
  266.     stream_Close(inStream);
  267.     MaxMem(&junk);
  268.     gprintf(TRANS,
  269.         "Phase 1 completed; %ld note(s) and %ld ctrl(s) translated; %lu bytes free\n",
  270.         notes, controls, FreeMem());
  271.     return newScore;
  272. }
  273.  
  274.  
  275. /* play_mode -- play score until user quits */
  276. /**/
  277. void play_mode()
  278. {
  279.     event_type score = new_score(NULL);
  280.     boolean done = false;
  281.     char response[100];
  282.     
  283.     startTime = 0;
  284.  
  285.     while (!done) {
  286.         gprintf(TRANS, "Type <Return> to play, ? for help, q to exit play mode:");
  287.         gets(response);
  288.         switch (*response) {
  289.           case 0:
  290.             play_pause_loop(score, startTime, NULL);
  291.             break;
  292.           case 'q':
  293.           case 'Q':
  294.             done = true;
  295.             break;
  296.           default:
  297.             default_response(*response);
  298.         }
  299.     }
  300. }
  301.     
  302. /****************************************************************************
  303. *                play_pause_loop
  304. * Inputs:
  305. *    event_type score: score to play
  306. *    ulong startTime: where to start playing
  307. *    StreamPtr outStream: stream to record to, if recording
  308. * Effect:
  309. *    plays the music, simultaneously recording if outStream isn't NULL
  310. ****************************************************************************/
  311.  
  312. private void play_pause_loop(score, startTime, outStream)
  313.     event_type score;
  314.     ulong startTime;
  315.     StreamPtr outStream;
  316. {
  317.     ulong stop_time;
  318.     event_type stop_place = score;
  319.     int stop_reason;
  320.     boolean proceed = false;
  321.  
  322.     if (startTime == 0) gprintf(TRANS, "Type <space> to stop, p to pause");
  323.     for (;;) {
  324.         gprintf(TRANS, "...");
  325.         phase2_Play(
  326.             stop_place, proceed, (outStream != NULL), startTime,
  327.             &stop_time, &stop_place, &stop_reason);
  328.         report_stop(score, stop_time, stop_reason);
  329.         if (stop_reason == PAUSE) {
  330.             char input[100];
  331.             proceed = true;
  332.             gets(input);
  333.         } else {
  334.             break;    /* get out of this loop */
  335.         }
  336.     }
  337. }
  338.  
  339. /* record_mode -- handle record commands until user exits */
  340. /**/
  341. void record_mode()
  342. {
  343.     boolean done = false;
  344.     char response[100];
  345.     event_type score = new_score(NULL);
  346.     
  347.     while (!done) {
  348.         gprintf(TRANS, "Type <Return> to record, ? for help, q to exit record mode:");
  349.         gets(response);
  350.         switch (*response) {
  351.           case 0:
  352.             {
  353.                 FILE *outFile;
  354.                 StreamPtr outStream;
  355.                 boolean do_pitch_bend = false;
  356.  
  357.                 outFile = fileopen("", "gio", "w", "File to record into");
  358.                 outStream = filestream_OpenWrite(outFile);
  359.                 if (outStream == NULL) {
  360.                     gprintf(FATAL, "Could not open output file.");
  361.                     return;
  362.                 }
  363.                 do_pitch_bend =
  364.                     askbool("Enable recording of pitch bend information",
  365.                             false);
  366.                 rec_init(do_pitch_bend);
  367.                 play_pause_loop(score, startTime, outStream);
  368.                 rec_final(outStream, true);
  369.             }
  370.             break;
  371.           case 'q':
  372.           case 'Q':
  373.             done = true;
  374.             break;
  375.           default:
  376.             default_response(*response);
  377.         }
  378.     }
  379. }
  380.  
  381.  
  382. /****************************************************************************
  383. *                report_activity
  384. * Effect:
  385. *    reports currently playing notes or recently played notes in each voice
  386. ****************************************************************************/
  387.  
  388. /* RECENTLY determines how far back to look for "recent" notes to report
  389.  * it is currently set to two seconds in hundreths of a second
  390.  */
  391. #define RECENTLY 200
  392.  
  393. /* structure used by report_activity: */
  394. struct ActivityRecord {
  395.     boolean        played;
  396.     boolean        reported;
  397.     int            line;
  398.     ulong        start_time;
  399. };
  400.  
  401. private void report_activity(score, stop_time)
  402.     event_type score;
  403.     ulong stop_time;
  404. {
  405.     struct ActivityRecord voice[MAX_CHANNELS + 1];
  406.         /* voice[] keeps the state of each voice at stop_time
  407.          * (NOTE: voice[0] is unused) */
  408.     event_type n;
  409.     int i;
  410.  
  411.     /* initialize voice record array */
  412.     for (i = 1; i <= MAX_CHANNELS; i++) {
  413.         voice[i].played = voice[i].reported = false;
  414.     }
  415.  
  416.     gprintf(TRANS, "At time %ld (hundreths of a second):\n", stop_time);
  417.  
  418.     for (n = score; n != NULL; n = n->next) {
  419.         if (is_note(n) &&
  420.             (n->ntime <= stop_time)) {
  421.             /* found a note which started before stop_time */
  422.             if ((n->ntime + n->u.note.ndur) >= stop_time) {
  423.                 /* report if it was playing at stop_time */
  424.                 gprintf(TRANS, "    Voice %d was playing line %d\n",
  425.                        n->nvoice, n->nline);
  426.                 voice[n->nvoice].reported = true;
  427.             } else {
  428.                 /* otherwise remember it; it might be the last note played */
  429.                 voice[n->nvoice].played = true;
  430.                 voice[n->nvoice].start_time = n->ntime;
  431.                 voice[n->nvoice].line = n->nline;
  432.             }
  433.         }
  434.     } /* end of score traversal */
  435.  
  436.     for (i = 1; i <= MAX_CHANNELS; i++) {
  437.         if ( voice[i].played &&
  438.             !voice[i].reported &&
  439.             (voice[i].start_time + RECENTLY >= stop_time))
  440.                 gprintf(TRANS, "    Voice %d recently played line %d\n",
  441.                         i, voice[i].line);
  442.     }
  443. }
  444.  
  445. /****************************************************************************
  446. *                report_modes
  447. * Effect:
  448. *    report state of various operation modes
  449. ****************************************************************************/
  450.  
  451. private void report_modes()
  452. {
  453.     gprintf(TRANS, "\n");
  454.     gprintf(TRANS, "MIDI byte trace (m option) %s\n",
  455.             (miditrace ? "on" : "off"));
  456.     gprintf(TRANS, "Music operation trace (t option) %s\n\n",
  457.             (musictrace ? "on" : "off"));
  458. }
  459.  
  460. /****************************************************************************
  461. *                report_stop
  462. * Inputs:
  463. *    event_type score: the score that was being played
  464. *    ulong stop_time: the time that playing was stopped
  465. *    int stop_reason: the reason that playing was stopped
  466. * Effect:
  467. *    announce stopped state and (under some conditions) tell
  468. *    what was playing (or recently played) when we stopped
  469. ****************************************************************************/
  470.  
  471. private void report_stop(score, stop_time, stop_reason)
  472.     event_type score;
  473.     ulong stop_time;
  474.     int stop_reason;
  475. {
  476.     gprintf(TRANS, "\n");
  477.     switch(stop_reason) {
  478.         case STOP_REQ:
  479.             gprintf(TRANS, "      * * STOPPED * *\n");
  480.             gprintf(TRANS, "Stopped at user's request\n");
  481.             break;
  482.         case OUT_OF_MEMORY:
  483.             gprintf(TRANS, "      * * STOPPED * *\n");
  484.             gprintf(TRANS, "Out of memory for recording\n");
  485.             break;
  486.         case PAUSE:
  487.             gprintf(TRANS, "Pause at %lu", stop_time);
  488.             return;    /* quit early -- don't print activity */
  489.             break;
  490.         case END_SCORE:
  491.             gprintf(TRANS, "Done!\n");
  492.             return;    /* quit early -- don't print activity */
  493.             break;
  494.         default:
  495.             /* never happens */
  496.             gprintf(FATAL,
  497.                 "Implementation error (adagio.c): unknown stop reason.");
  498.             clean_exit();
  499.             break;
  500.     }
  501.  
  502.     if (stop_time > 0) {
  503.         report_activity(score, stop_time);
  504.     }
  505. }
  506.  
  507. /****************************************************************************
  508. *                    main
  509. * Inputs:
  510. *    int argc: count of arguments
  511. *    char * argv[]: vector of argument pointers
  512. * Effect:
  513. *    runs adagio
  514. ****************************************************************************/
  515.  
  516. public void _main(argc, argv)
  517.    int argc;
  518.    char *argv[];
  519. {
  520.     event_type score = NULL;
  521.  
  522. #ifdef MPW
  523.     setvbuf(stdout, NULL, _IOLBF, 0);
  524.         /* set i/o buffering to dump output buffer every line */
  525. #endif
  526.  
  527.     /* command line processing: */
  528.     cl_init(switches, nswitches, options, noptions, argv, argc);
  529.     if (cl_switch("-help") || cl_switch("-h")) {
  530.         cmdline_help();
  531.         return;
  532.     }
  533.     if (cl_switch("-simulated") || cl_switch("-s")) {
  534.         mpuexists(false);
  535.     }
  536.  
  537.     MaxApplZone();            /* use all possible space */
  538.     timereset();            /* reset time */
  539.     off_init();                /* initialize note-off event manager */
  540.     if (cl_arg(1) != NULL) {
  541.         /* if file name argument given, try to read score from that file */
  542.         /* score = new_score(cl_arg(1)); */
  543.         gprintf("Ignoring '%s' in switch list\n", cl_arg(1));
  544.     }
  545.     cmd_loop(score);        /* git down and BOOGEY! */
  546.     phase1_FreeMem();
  547. }
  548.  
  549.  
  550. void transcribe_mode()
  551. {
  552.     boolean done = false;
  553.     char response[100];
  554.     
  555.     while (!done) {
  556.         gprintf(TRANS,
  557.  "Type <Return> to transcribe, ? for help, q to exit transcribe mode:");
  558.         gets(response);
  559.  
  560.         switch (*response) {
  561.           case 0:
  562.             {
  563.                 FILE *outFile;
  564.                 StreamPtr outStream;
  565.                 boolean do_pitch_bend = false;
  566.  
  567.                 outFile = fileopen("", "gio", "w", "File to record into");
  568.                 outStream = filestream_OpenWrite(outFile);
  569.                 if (outStream == NULL) {
  570.                     gprintf(FATAL, "Could not open output file.");
  571.                     return;
  572.                 }
  573.                 do_pitch_bend =
  574.                     askbool("Enable recording of pitch bend information",
  575.                             false);
  576.                 rec_init(do_pitch_bend);
  577.                 play_pause_loop(NULL, 0, outStream);
  578.                 rec_final(outStream, false);
  579.             }
  580.             break;
  581.           case 'q':
  582.           case 'Q':
  583.             done = true;
  584.             break;
  585.           default:
  586.             default_response(*response);
  587.         }
  588.     }
  589. }
  590.